﻿using DataRepositoryLayer.Classes;
using DataRepositoryLayer.Classes;
using DataRepositoryLayer.Interfaces;
using ModelLayer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace BusinessLogicLayer
{
    public class AccountManager
    {
        public void SendConformationEmail(UserAccount account)
        {
            using(SmtpClient client = new SmtpClient("smtp.gmail.com"))
            {
                client.Port = 587;
                client.UseDefaultCredentials = false;
                client.Credentials = new System.Net.NetworkCredential("bes7group@gmail.com", "terryPratchet");
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                client.EnableSsl = true;
                using (MailMessage message = new MailMessage())
                {
                    message.To.Add(account.email);
                    message.Subject = "Welcome to Group 7 Electric Car Management System!";
                    message.From = new MailAddress("bes7group@gmail.com");
                    string confirmationMessage = "Thank you for registering in our system, please click on this link to confirm your registration.\n";
                    confirmationMessage += "http://localhost:8733/Design_Time_Addresses/WcfServiceLayer/Service1/ConfirmEmail/";
                    confirmationMessage += account.session_token;
                    message.Body = confirmationMessage;
                    client.Send(message);
                }
            }
        }
        public IList<UserAccount> GetAllUserAccounts()
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository accountRepository = new UserAccountRepository(context);
                return accountRepository.GetForManagementSystem();
            }
        }

        public UserAccount LogInUser(string username, string password)
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository userRep = new UserAccountRepository(context);
                UserAccount account = userRep.GetByUsername(username);
                if (account == null || (account.session_token != null && account.session_token.StartsWith("_")))
                {
                    return null;
                }
                string passHash = account.pass;
                if (CheckPassword(passHash, password))
                {
                    account.session_token = GenerateSessionToken();
                    userRep.Update(account);
                    context.Commit();
                    return account;
                }
                return null;
            }
        }

        public EmployeeAccount LogInEmployee(string username, string password)
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                EmployeeAccount account =  empRep.GetByUsername(username);
                if(account == null)
                {
                    return null;
                }
                string passHash = account.pass;
                if(CheckPassword(passHash, password))
                {
                    account.session_token = GenerateSessionToken();
                    empRep.Update(account);
                    context.Commit();
                    return account;
                }
                return null;
            }
        }

        public void CreateEmployeeAccount(EmployeeAccount account)
        {
            account.pass = HashPassword(account.pass, GenerateSalt(64));
            using(IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                empRep.Add(account);
                context.Commit();
            }
        }

        public bool SignUserUp(UserAccount account)
        {
            account.pass = HashPassword(account.pass, GenerateSalt(64));
            account.session_token = GenerateEmailToken();
            account.registration_date = DateTime.Now;
            using(IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository usrRep = new UserAccountRepository(context);
                if(usrRep.IsUsernameTaken(account))
                {
                    return false;
                }
                usrRep.Add(account);
                context.Commit();
            }
            SendConformationEmail(account);
            return true;
        }

        public void UpdateEmployeeAccount(EmployeeAccount account)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                if(account.pass != null)
                {
                    account.pass = HashPassword(account.pass, GenerateSalt(64));
                }
                else
                {
                    account.pass = empRep.GetById(account.id).pass;
                }
                empRep.Update(account);
                context.Commit();
            }
        }

        public void DeleteEmployeeAccount(EmployeeAccount account)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                empRep.Remove(account);
                context.Commit();
            }
        }

        public IList<EmployeeAccount> GetAllEmployeeAccounts()
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                return empRep.GetAllAccountsForManagementSystem();
            }
        }

        public bool CheckEmployeeSessionToken(string sessionToken)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                return empRep.CheckSessionToken(sessionToken) != null;
            }
        }

        public EmployeeAccount ReloadAccount(string sessionToken)
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IEmployeeAccountRepository empRep = new EmployeeAccountRepository(context);
                return empRep.CheckSessionToken(sessionToken);
            }
        }

        

        #region Bussiness logic code

        private string HashPassword(string password, string salt)
        {
            byte[] bytePass = ASCIIEncoding.ASCII.GetBytes(password + salt);
            byte[] byteHashPass = SHA256CryptoServiceProvider.Create().ComputeHash(bytePass);
            return ByteArrayToString(byteHashPass) + salt;
        }

        private string GenerateSalt(int size)
        {
            string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            char[] stringChars = new char[size];
            var random = new Random();

            for (int i = 0; i < size; i++)
            {
                stringChars[i] = chars[random.Next(chars.Length)];
            }

            return  new String(stringChars);
        }

        private bool CheckPassword(string passHash, string password)
        {
            string salt = passHash.Substring(64);
            return passHash == HashPassword(password, salt);
        }

        private string ByteArrayToString(byte[] arrayInput)
        {
            StringBuilder output = new StringBuilder(arrayInput.Length);
            for (int i = 0; i < arrayInput.Length; i++)
            {
                output.Append(arrayInput[i].ToString("X2"));
            }
            return output.ToString();
        }

        private string GenerateSessionToken()
        {
            return GenerateSalt(10) + DateTime.Now.Ticks;
        }

        private string GenerateEmailToken()
        {
            return "_" + GenerateSalt(9) + DateTime.Now.Ticks;
        }

        #endregion



        public void ConfirmAccount(string sessionToken)
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository usrRep = new UserAccountRepository(context);
                UserAccount account = usrRep.CheckEmailToken(sessionToken);
                if(account != null)
                {
                    account.session_token = null;
                    usrRep.Update(account);
                    context.Commit();
                }
            }
        }
    }
}
